#include "q_shared.h"
#include "bg_global.h"
#include "bg_hooks.h"
#include "bg_gamex86funcs.h"
#include "g_local.h"

typedef struct {
	char	*name;
	unsigned int spawn;
} spawn_t;



spawn_t spawns[] = {
 { "info_player_start", 0x20057510 },
 { "info_player_deathmatch", 0x20057450 },
 { "func_plat", 0x2007E680 },
 { "func_button", 0x2007E7C0 },
 { "func_door", 0x2007E3F0 },
 { "func_static", 0x2007EB80 },
 { "func_rotating", 0x2007EC70 },
 { "func_bobbing", 0x2007ED90 },
 { "func_breakable", 0x20056080 },
 { "func_pendulum", 0x2007EEF0 },
 { "func_train", 0x2007E930 },
 { "func_timer", 0x20098A80 },
 { "func_wall", 0x2007F010 },
 { "func_usable", 0x2009E550 },
 { "func_glass", 0x20055760 },
 { "rail_mover", 0x2008D340 },
 { "rail_track", 0x2008D080 },
 { "rail_lane", 0x2008D1A0 },
 { "trigger_always", 0x20098250 },
 { "trigger_multiple", 0x20099550 },
 { "trigger_once", 0x200996D0 },
 { "trigger_push", 0x20099C40 },
 { "trigger_teleport", 0x20099CD0 },
 { "trigger_hurt", 0x20099D20 },
 { "trigger_bidirectional", 0x200CE030 },
 { "trigger_entdist", 0x20098CD0 },
 { "trigger_location", 0x200981D0 },
 { "trigger_visible", 0x20098E80 },
 { "trigger_space", 0x20099E00 },
 { "trigger_shipboundary", 0x20099E30 },
 { "target_give", 0x20096450 },
 { "target_delay", 0x20096480 },
 { "target_speaker", 0x20097B20 },
 { "target_print", 0x200965A0 },
 { "target_laser", 0x200965D0 },
 { "target_score", 0x20096510 },
 { "target_teleporter", 0x20096650 },
 { "target_relay", 0x200967C0 },
 { "target_kill", 0x20096940 },
 { "target_position", 0x20096950 },
 { "target_location", 0x20096970 },
 { "target_push", 0x20098330 },
 { "target_random", 0x20096BE0 },
 { "target_counter", 0x20096A80 },
 { "target_scriptrunner", 0x20096E00 },
 { "target_interest", 0x2003E450 },
 { "target_activate", 0x2009F460 },
 { "target_deactivate", 0x2009F480 },
 { "target_gravity_change", 0x20096F30 },
 { "target_friction_change", 0x20096FB0 },
 { "target_level_change", 0x200975B0 },
 { "target_change_parm", 0x20097780 },
 { "target_play_music", 0x200977E0 },
 { "target_autosave", 0x200978D0 },
 { "target_secret", 0x20097940 },
 { "light", 0x200737D0 },
 { "info_null", 0x200735D0 },
 { "func_group", 0x200735D0 },
 { "info_notnull", 0x20096950 },
 { "path_corner", 0x2007C750 },
 { "misc_teleporter", 0x200738D0 },
 { "misc_teleporter_dest", 0x200739E0 },
 { "misc_model", 0x200CE030 },
 { "misc_model_static", 0x20073A10 },
 { "misc_turret", 0x2009E150 },
 { "misc_ns_turret", 0x2009CC20 },
 { "misc_laser_arm", 0x2009A530 },
 { "misc_ion_cannon", 0x2009D870 },
 { "misc_sentry_turret", 0x2009D170 },
 { "misc_maglock", 0x20075910 },
 { "misc_weapon_shooter", 0x200A2B60 },
 { "misc_weather_zone", 0x200769B0 },
 { "misc_model_ghoul", 0x20078190 },
 { "misc_model_breakable", 0x20056AB0 },
 { "misc_portal_surface", 0x20073AE0 },
 { "misc_portal_camera", 0x20073B90 },
 { "misc_bsp", 0x20073C10 },
 { "terrain", 0x20073DE0 },
 { "misc_skyportal", 0x200741A0 },
 { "misc_camera_focus", 0x200573D0 },
 { "misc_camera_track", 0x20057410 },
 { "misc_dlight", 0x20074C30 },
 { "misc_replicator_item", 0x200756F0 },
 { "misc_trip_mine", 0x20077000 },
 { "misc_security_panel", 0x2007CA50 },
 { "misc_camera", 0x200748A0 },
 { "misc_spotlight", 0x2009A9C0 },
 { "misc_panel_turret", 0x2009B130 },
 { "misc_model_welder", 0x20076370 },
 { "misc_model_jabba_cam", 0x20077F30 },
 { "misc_model_shield_power_converter", 0x20075B30 },
 { "misc_model_ammo_power_converter", 0x20076220 },
 { "misc_model_bomb_planted", 0x20075C40 },
 { "misc_model_beacon", 0x20075E70 },
 { "misc_shield_floor_unit", 0x20075F60 },
 { "misc_ammo_floor_unit", 0x20076290 },
 { "misc_model_gun_rack", 0x200786E0 },
 { "misc_model_ammo_rack", 0x200781E0 },
 { "misc_model_cargo_small", 0x20078ED0 },
 { "misc_exploding_crate", 0x20076490 },
 { "misc_gas_tank", 0x200766A0 },
 { "misc_crystal_crate", 0x20076870 },
 { "misc_atst_drivable", 0x20077AF0 },
 { "ref_tag", 0x2008F770 },
 { "NPC_spawner", 0x20042900 },
 { "NPC_Vehicle", 0x2003FF70 },
 { "NPC_Player", 0x20042B20 },
 { "NPC_Kyle", 0x20042B40 },
 { "NPC_Lando", 0x20042B80 },
 { "NPC_Jan", 0x20042BA0 },
 { "NPC_Luke", 0x20042BC0 },
 { "NPC_MonMothma", 0x20042BE0 },
 { "NPC_Rosh_Penin", 0x20042C00 },
 { "NPC_Tavion", 0x20042C50 },
 { "NPC_Tavion_New", 0x20042C70 },
 { "NPC_Alora", 0x20042CE0 },
 { "NPC_Reelo", 0x20042D00 },
 { "NPC_Desann", 0x20042D20 },
 { "NPC_Rax", 0x20042D40 },
 { "NPC_BobaFett", 0x20042D60 },
 { "NPC_Ragnos", 0x20042D80 },
 { "NPC_Lannik_Racto", 0x20042DA0 },
 { "NPC_Kothos", 0x20042DC0 },
 { "NPC_Chewbacca", 0x20042E00 },
 { "NPC_Bartender", 0x20042E20 },
 { "NPC_MorganKatarn", 0x20042E40 },
 { "NPC_Jedi", 0x20042E60 },
 { "NPC_Prisoner", 0x20043010 },
 { "NPC_Merchant", 0x200430A0 },
 { "NPC_Rebel", 0x200430C0 },
 { "NPC_Human_Merc", 0x200430F0 },
 { "NPC_Stormtrooper", 0x200431A0 },
 { "NPC_StormtrooperOfficer", 0x20043250 },
 { "NPC_Tie_Pilot", 0x20043290 },
 { "NPC_Snowtrooper", 0x20043270 },
 { "NPC_RocketTrooper", 0x200432B0 },
 { "NPC_HazardTrooper", 0x200432F0 },
 { "NPC_Ugnaught", 0x20043350 },
 { "NPC_Jawa", 0x200433A0 },
 { "NPC_Gran", 0x200433E0 },
 { "NPC_Rodian", 0x20043460 },
 { "NPC_Weequay", 0x200434A0 },
 { "NPC_Trandoshan", 0x20043530 },
 { "NPC_Tusken", 0x20043560 },
 { "NPC_Noghri", 0x200435A0 },
 { "NPC_SwampTrooper", 0x200435D0 },
 { "NPC_Imperial", 0x20043610 },
 { "NPC_ImpWorker", 0x20043670 },
 { "NPC_BespinCop", 0x20043690 },
 { "NPC_Reborn", 0x200436E0 },
 { "NPC_Reborn_New", 0x20043770 },
 { "NPC_Cultist", 0x200439A0 },
 { "NPC_Cultist_Saber", 0x20043860 },
 { "NPC_Cultist_Saber_Powers", 0x20043900 },
 { "NPC_Cultist_Destroyer", 0x20043AC0 },
 { "NPC_Cultist_Commando", 0x20043A90 },
 { "NPC_ShadowTrooper", 0x20043AE0 },
 { "NPC_Saboteur", 0x20043B30 },
 { "NPC_Monster_Murjj", 0x20043BA0 },
 { "NPC_Monster_Swamp", 0x20043BC0 },
 { "NPC_Monster_Howler", 0x20043BE0 },
 { "NPC_Monster_Rancor", 0x20043C00 },
 { "NPC_Monster_Mutant_Rancor", 0x20043C40 },
 { "NPC_Monster_Wampa", 0x20043C60 },
 { "NPC_MineMonster", 0x20043C80 },
 { "NPC_Monster_Claw", 0x20043CA0 },
 { "NPC_Monster_Glider", 0x20043CC0 },
 { "NPC_Monster_Flier2", 0x20043CE0 },
 { "NPC_Monster_Lizard", 0x20043D00 },
 { "NPC_Monster_Fish", 0x20043D20 },
 { "NPC_Monster_Sand_Creature", 0x20043D40 },
 { "NPC_Droid_Interrogator", 0x20043D80 },
 { "NPC_Droid_Probe", 0x20043DA0 },
 { "NPC_Droid_Mark1", 0x20043DC0 },
 { "NPC_Droid_Mark2", 0x20043DE0 },
 { "NPC_Droid_ATST", 0x20043E00 },
 { "NPC_Droid_Seeker", 0x20043E40 },
 { "NPC_Droid_Remote", 0x20043E20 },
 { "NPC_Droid_Sentry", 0x20043E60 },
 { "NPC_Droid_Gonk", 0x20043E80 },
 { "NPC_Droid_Mouse", 0x20043EA0 },
 { "NPC_Droid_R2D2", 0x20043EC0 },
 { "NPC_Droid_R5D2", 0x20043F00 },
 { "NPC_Droid_Protocol", 0x20043F40 },
 { "NPC_Droid_Assassin", 0x20043F80 },
 { "NPC_Droid_Saber", 0x20043FB0 },
 { "waypoint", 0x2007F550 },
 { "waypoint_small", 0x2007F200 },
 { "waypoint_navgoal", 0x2007F2F0 },
 { "fx_runner", 0x2006BC90 },
 { "fx_explosion_trail", 0x2006B380 },
 { "fx_target_beam", 0x2006B6B0 },
 { "fx_cloudlayer", 0x2006B810 },
 { "fx_rain", 0x2006C3E0 },
 { "fx_wind", 0x2006BDE0 },
 { "fx_snow", 0x2006B080 },
 { "fx_puff", 0x2006C5F0 },
 { "fx_wind_zone", 0x2006BF50 },
 { "object_cargo_barrel1", 0x20074AD0 },
 { "point_combat", 0x200377C0 },
 { "emplaced_gun", 0x20069E50 },
 { "emplaced_eweb", 0x2006975 },
 { NULL, NULL }
};

void InitSpawnFunc( void )
{
	SP_NPC_Spawner = (SpawnFunc)DLLA(0x20042900, jagamex86);
	SP_waypoint_navgoal = (SpawnFunc)DLLA(0x2007F2F0, jagamex86);
	SP_waypoint = (SpawnFunc)DLLA(0x2007F550, jagamex86);
}

qboolean G_CallSpawn( gentity_t *ent )
{
	spawn_t *s;
	gitem_t *item;
	SpawnFunc spawnfunc;
	qboolean OK = qfalse;
	if(!ent->classname)
	{
		gi->Printf("^1G_CallSpawn: NULL classname\n");
		return qfalse;
	}

	//Check item spawn funcs
	for(item=bg_itemlist; item->classname; item++)
	{
		if(!strcmp(item->classname, ent->classname))
		{
			//Found it :P
			G_SpawnItem( ent, item );
			return qtrue;
		}
	}

	//Check normal spawn functions
	for ( s=spawns ; s->name ; s++ ) {
		if ( !strcmp(s->name, ent->classname) ) {
			// found it
			spawnfunc = (SpawnFunc)DLLA(s->spawn, jagamex86);
			OK = qtrue;
		}
	}
	//Check extra spawn functions
	if(!Q_stricmp(ent->classname, "NPC_Galak"))
	{
		SP_NPC_Galak( ent );
		return qtrue;
	}
	else if(!Q_stricmp(ent->classname, "waypoint_navgoal_8"))
	{
		SP_waypoint_navgoal_8( ent );
		return qtrue;
	}
	else if(!Q_stricmp(ent->classname, "waypoint_navgoal_4"))
	{
		SP_waypoint_navgoal_4( ent );
		return qtrue;
	}
	else if(!Q_stricmp(ent->classname, "waypoint_navgoal_2"))
	{
		SP_waypoint_navgoal_2( ent );
		return qtrue;
	}
	else if(!Q_stricmp(ent->classname, "waypoint_navgoal_1"))
	{
		SP_waypoint_navgoal_1( ent );
		return qtrue;
	}
	else if(!Q_stricmp(ent->classname, "waypoint_navgoal"))
	{
		SP_waypoint_navgoal_1( ent );
		return qtrue;
	}
	else if(!Q_stricmp(ent->classname, "waypoint"))
	{
		if(SP_Waypoint( ent ))
		{
			return qtrue;
		}
		//Keep on going otherwise
	}
	else if(!Q_stricmp(ent->classname, "misc_model_gun_rack2"))
	{
		SP_misc_model_gun_rack2( ent );
		return qtrue;
	}
	else if(!Q_stricmp(ent->classname, "misc_model_gun_rack"))
	{
		SP_misc_model_gun_rack2( ent );
		return qtrue;
	}
	if(!OK)
	{
		gi->Printf("^1ERROR: %s does not have a spawn function!\n", ent->classname);
		*delayedShutdown = *level_time + 100;
		return qfalse;
	}
	spawnfunc(ent);
	return qtrue;
}

static int exitAddy1;// = 0x2005C867;
static int exitAddy2;// = 0x2009354C;
static int exitAddy3;// = 0x200936D5;

__declspec(naked) void __cdecl hook_G_CallSpawn1(gentity_t *ent)
{
	__asm {
		call G_CallSpawn
		jmp exitAddy1
	}
}

__declspec(naked) void __cdecl hook_G_CallSpawn2(gentity_t *ent)
{
	__asm {
		call G_CallSpawn
		jmp exitAddy2
	}
}

__declspec(naked) void __cdecl hook_G_CallSpawn3(gentity_t *ent)
{
	__asm {
		call G_CallSpawn
		jmp exitAddy3
	}
}

const BYTE NOP = 0x90;

void HookSpawningFuncs( void )
{
	InitSpawnFunc();
	//G_CallSpawn
	//G_CallSpawn is called in three places:
	//- 0x2005C862
	//- 0x20093547
	//- 0x200936D0
	exitAddy1 = DLLA(0x2005C867, jagamex86);
	exitAddy2 = DLLA(0x2009354C, jagamex86);
	exitAddy3 = DLLA(0x200936D5, jagamex86);
	HOOK_JUMP(HOOK_G_CALLSPAWN1, DLLA(0x2005C862, jagamex86), hook_G_CallSpawn1);
	HOOK_JUMP(HOOK_G_CALLSPAWN2, DLLA(0x20093547, jagamex86), hook_G_CallSpawn2);
	HOOK_JUMP(HOOK_G_CALLSPAWN3, DLLA(0x200936D0, jagamex86), hook_G_CallSpawn3);
	HOOK_CALL(HOOK_FIXED_SABER_CRASH, DLLA(0x200700D3, jagamex86), Q_stricmp_safe);
}


//VirtualProtect( addr, 5, PAGE_EXECUTE_READWRITE, (PDWORD)dummy );
//*(unsigned int *)(lpAddress+1) = ((unsigned int)<your naked func> - (unsigned int)(lpAddress + 5));
//lpAddress = addr
//VirtualProtect( lpAddress, size, PAGE_EXECUTE_READ, (PDWORD)dummy );
//size = 5
//also if it's a cdecl do *lpAddress = 0xE9;